import Cesium, { Geometry } from '../Ces/Cesium'
import WeFramebuffer from '../Ces/WeFramebuffer'

type WeCustomPrimitiveOptions = {
  fragmentShaderSource: string
  vertexShaderSource?: string
  geometry?: Geometry
  uniformMap?: {}
}

class WeCustomPrimitive {
  private geometry: Geometry
  private clearCommand: any
  private commandToExecute: any
  private dirty: boolean = false
  private framebuffer: WeFramebuffer
  private vertexShaderSource: string
  private fragmentShaderSource: string
  private uniformMap: {}
  constructor(options: WeCustomPrimitiveOptions) {
    this.vertexShaderSource = options.vertexShaderSource
    this.fragmentShaderSource = options.fragmentShaderSource
    this.geometry = options.geometry
    this.uniformMap = options.uniformMap ?? {}
    this.dirty = true
  }

  rebuild(context) {
    const { geometry, uniformMap } = this
    this.commandToExecute = this.commandToExecute?.shaderProgram.destroy()

    const { width, height } = context.canvas
    const textureOptions = {
      context,
      width,
      height,
      pixelFormat: Cesium.PixelFormat.RGBA,
      pixelDatatype: Cesium.PixelDatatype.HALF_FLOAT,
    }

    const colorTexture0 = new Cesium.Texture(textureOptions)

    const framebuffer = new Cesium.Framebuffer({
      context,
      colorTextures: [colorTexture0],
      destroyAttachments: false,
    })

    const fragmentShaderSource = this.fragmentShaderSource

    let vertexArray
    let attributeLocations
    let vertexShaderSource
    let renderState
    if (geometry) {
      attributeLocations = {
        position: 0,
      }
      vertexArray = Cesium.VertexArray.fromGeometry({
        context,
        geometry,
        attributeLocations,
        bufferUsage: Cesium.BufferUsage.STATIC_DRAW,
      })
      vertexShaderSource = this.vertexShaderSource

      renderState = Cesium.RenderState.fromCache({
        depthTest: { enabled: true },
        depthMask: true,
        blending: Cesium.BlendingState.ALPHA_BLEND,
        cull: {
          enabled: false,
          face: Cesium.CullFace.FRONT,
        },
      })
    } else {
      vertexArray = context.getViewportQuadVertexArray()
      vertexShaderSource = Cesium._shadersViewportQuadVS
      attributeLocations = {
        position: 0,
        textureCoordinates: 1,
      }
      renderState = Cesium.RenderState.fromCache({
        viewport: new Cesium.BoundingRectangle(0, 0, width, height),
      })
    }

    const fragmentShader = new Cesium.ShaderSource({
      defines: ["LOG_DEPTH"],
      sources: [fragmentShaderSource],
    })

    const shaderProgram = Cesium.ShaderProgram.fromCache({
      context,
      vertexShaderSource,
      fragmentShaderSource: fragmentShader,
      attributeLocations,
    })

    const translucent = true
    this.commandToExecute = new Cesium.DrawCommand({
      owner: this,
      vertexArray,
      primitiveType: Cesium.PrimitiveType.TRIANGLES,
      uniformMap,
      shaderProgram,
      // framebuffer,
      renderState,
      pass: translucent ? Cesium.Pass.TRANSLUCENT : Cesium.Pass.OPAQUE,
    })

    this.framebuffer = framebuffer
  }

  update(frameState) {
    if (this.dirty) {
      this.dirty = false
      this.rebuild(frameState.context)
    }

    if (this.clearCommand) {
      frameState.commandList.push(this.clearCommand)
    }
    if (this.commandToExecute) {
      frameState.commandList.push(this.commandToExecute)
    }
  }

  isDestroyed() {
    return false
  }
  destroy() {
    this.framebuffer = this.framebuffer?.destroy()
    this.commandToExecute = this.commandToExecute?.shaderProgram.destroy()
    return Cesium.destroyObject(this)
  }
}

export default WeCustomPrimitive
